import axios, {
    type AxiosRequestConfig,
    type AxiosResponse,
    type AxiosError,
} from "axios";
import { mergeConfig } from "./index.ts";
import { TIME_OUT, RES_CODE } from "../config/service.ts";
import { REQUEST_URL } from "../config/index.ts";
import type { ApiResponse } from "../types/response";

// ========================
// HTTP 请求服务类 - Web端axios版
// ========================

interface HttpConfig extends AxiosRequestConfig {
    errorMap?: Record<number, string>;
    retryConfig?: {
        retry: number;
        delay: number;
        retryableStatusCodes: number[];
    };
}

type RequestInterceptor = (
    config: AxiosRequestConfig
) => AxiosRequestConfig | Promise<AxiosRequestConfig>;
type ResponseInterceptor = (response: AxiosResponse) => any | Promise<any>;
type ErrorInterceptor = (error: AxiosError) => any | Promise<any>;

class HttpService {
    private defaultConfig: HttpConfig;
    private config: HttpConfig;
    private interceptors: {
        request: RequestInterceptor[];
        response: ResponseInterceptor[];
        error: ErrorInterceptor[];
    };

    constructor(config: HttpConfig = {}) {
        this.defaultConfig = {
            baseURL: REQUEST_URL,
            timeout: Number(TIME_OUT),
            headers: {
                "Content-Type": "application/json;charset=UTF-8",
            },
            errorMap: {
                400: "请求参数错误",
                401: "未授权，请重新登录",
                403: "拒绝访问",
                404: "请求资源不存在",
                500: "服务器内部错误",
                502: "网关错误",
                503: "服务不可用",
                504: "网关超时",
            },
            retryConfig: {
                retry: 3,
                delay: 1000,
                retryableStatusCodes: [408, 500, 502, 503, 504],
            },
        };
        this.config = mergeConfig(this.defaultConfig, config);
        this.interceptors = {
            request: [],
            response: [],
            error: [],
        };
    }

    useRequestInterceptor(interceptor: RequestInterceptor) {
        if (typeof interceptor === "function") {
            this.interceptors.request.push(interceptor);
        }
    }

    useResponseInterceptor(
        successInterceptor?: ResponseInterceptor,
        errorInterceptor?: ErrorInterceptor
    ) {
        if (typeof successInterceptor === "function") {
            this.interceptors.response.push(successInterceptor);
        }
        if (typeof errorInterceptor === "function") {
            this.interceptors.error.push(errorInterceptor);
        }
    }

    async runInterceptorChain<T>(
        chain: Array<(arg: T) => T | Promise<T>>,
        initialValue: T
    ): Promise<T> {
        let value = initialValue;
        for (const interceptor of chain) {
            value = await interceptor(value);
        }
        return value;
    }

    buildUrlWithParams(url: string, params?: Record<string, any>): string {
        if (!params) return url;
        const query = Object.keys(params)
            .map(
                (key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`
            )
            .join("&");
        return query ? `${url}${url.includes("?") ? "&" : "?"}${query}` : url;
    }

    async request<T = any>(options: AxiosRequestConfig = {}): Promise<T> {
        let config: AxiosRequestConfig = {
            ...this.config,
            ...options,
            headers: {
                ...this.config.headers,
                ...(options.headers || {}),
            },
        };
        // 过滤 header 中 undefined/null
        Object.keys(config.headers || {}).forEach((key) => {
            if (config.headers && (config.headers as any)[key] == null)
                delete (config.headers as any)[key];
        });
        // 拼接 baseURL
        if (
            config.baseURL &&
            config.url &&
            !config.url.startsWith("http") &&
            !config.url.startsWith("//")
        ) {
            config.url = config.baseURL + config.url;
        }
        // GET/DELETE 拼接 query
        if (
            (config.method === "GET" || config.method === "DELETE") &&
            (config as any).params
        ) {
            config.url = this.buildUrlWithParams(config.url!, (config as any).params);
            delete (config as any).params;
        }
        // 请求拦截器
        config = await this.runInterceptorChain(this.interceptors.request, config);
        let retryCount = 0;
        const retryConfig = (this.config as any).retryConfig || { retry: 0 };
        while (true) {
            try {
                const response = await axios.request<ApiResponse<T>>(config);
                return (await this.runInterceptorChain(
                    this.interceptors.response,
                    response
                )) as T;
            } catch (error: any) {
                const status = error?.response?.status;
                if (
                    retryConfig.retryableStatusCodes?.includes(status) &&
                    retryCount < retryConfig.retry
                ) {
                    retryCount++;
                    await new Promise((res) => setTimeout(res, retryConfig.delay));
                    continue;
                }
                await this.runInterceptorChain(this.interceptors.error, error);
                throw error;
            }
        }
    }

    get<T = any>(url: string, params?: any, options: AxiosRequestConfig = {}) {
        return this.request<T>({ url, params, method: "GET", ...options });
    }
    post(url: string, data?: any, options: AxiosRequestConfig = {}) {
        return this.request({ url, data, method: "POST", ...options });
    }
    put(url: string, data?: any, options: AxiosRequestConfig = {}) {
        return this.request({ url, data, method: "PUT", ...options });
    }
    delete(url: string, data?: any, options: AxiosRequestConfig = {}) {
        return this.request({ url, data, method: "DELETE", ...options });
    }
    upload(
        url: string,
        file: File,
        name = "file",
        formData: Record<string, any> = {},
        options: AxiosRequestConfig = {}
    ) {
        const data = new FormData();
        data.append(name, file);
        Object.keys(formData).forEach((key) => {
            data.append(key, formData[key]);
        });
        return this.request({
            url,
            data,
            method: "POST",
            headers: {
                ...options.headers,
                "Content-Type": "multipart/form-data",
            },
            ...options,
        });
    }
}

// ========================
// 创建请求实例
// ========================
const http = new HttpService();

// 添加请求拦截器
http.useRequestInterceptor(async (config) => {
    // 添加认证Token
    const token = localStorage.getItem("token");
    if (token && !(config as any).noAuth) {
        config.headers = config.headers || {};
        (config.headers as any).Authorization = `Bearer ${token}`;
    }
    // 处理GET请求数组参数
    if (config.method === "GET" && config.params) {
        Object.keys(config.params).forEach((key) => {
            if (Array.isArray(config.params[key])) {
                config.params[key] = config.params[key].join(",");
            }
        });
    }
    // 添加请求时间戳
    (config as any).timestamp = Date.now();
    return config;
});

// 添加响应拦截器
http.useResponseInterceptor((response: AxiosResponse<ApiResponse>) => {
    const res = response.data;
    if (res[RES_CODE] === 0 || res[RES_CODE] === 200) {
        return res.data;
    } else {
        throw new Error(res.message || res.msg || "业务处理失败");
    }
});

// 添加错误拦截器
http.useResponseInterceptor(undefined, (error: AxiosError) => {
    // 统一错误处理
    if (!(error.config as any)?.hideErrorToast) {
        // 这里可以用自定义UI库弹窗或console
        console.error(error.message || "请求失败");
    }
    // 特定状态码处理
    if (error.response?.status === 404) {
        console.warn("请求的资源不存在:", error.config?.url);
    }
    // 记录错误日志
    console.error("HTTP请求错误:", {
        url: error.config?.url,
        method: error.config?.method,
        statusCode: error.response?.status,
        message: error.message,
        timestamp: new Date().toISOString(),
    });
    throw error;
});

// ========================
// 导出请求实例
// ========================
export default http;
